/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.entity.changeset;

import com.inspur.edp.cef.entity.accessor.base.IAccessor;

import com.inspur.edp.cef.entity.i18n.MultiLanguageInfo;
import java.util.LinkedHashMap;
import java.util.Map;

import com.inspur.edp.cef.entity.exception.CefExceptionBase;
import io.iec.edp.caf.boot.context.CAFContext;

//TODO: 先从bef拷贝过来, 得修改支持valueObject;
public final class ChangeDetailMerger
{
	public static IChangeDetail mergeChangeDetail(IChangeDetail source, IChangeDetail target)
	{
		if (source == null)
		{
			return target;
		}
		if (target == null)
		{
			return source;
		}
		if (source==target)
		{
			return source;
		}
		switch (source.getChangeType())
		{
			case Added:
				return mergeChangeByAddedSource((AddChangeDetail)((source instanceof AddChangeDetail) ? source : null), target);
			case Modify:
				return mergeChangeByModifiedSource((AbstractModifyChangeDetail)((source instanceof AbstractModifyChangeDetail) ? source : null), target);
			case Deleted:
				return mergeChangeByDeletedSource((DeleteChangeDetail)((source instanceof DeleteChangeDetail) ? source : null), target);
			default:
				throw new CefExceptionBase();
		}
	}

	private static IChangeDetail mergeChangeByAddedSource(AddChangeDetail source, IChangeDetail target) {
		if (target != null) {
			throw new CefExceptionBase("reAdd:"+target.getDataID());
		}
		return source.clone();
	}

	private static IChangeDetail mergeChangeByModifiedSource(AbstractModifyChangeDetail source, IChangeDetail target)
	{
		switch (target.getChangeType())
		{
			case Added:

				IAccessor acs = (IAccessor)((((AddChangeDetail)target).getEntityData() instanceof IAccessor) ? ((AddChangeDetail)target).getEntityData() : null);
				if(acs != null)
				{
					acs.acceptChange(source);
				}
				return target;
			case Modify:
				return mergeModifyChangeDetails(source, (AbstractModifyChangeDetail)((target instanceof AbstractModifyChangeDetail) ? target : null));
			//case ChangeType.Deleted:
			//    throw new ArgumentException();
			default:
				throw new IllegalArgumentException();
		}
	}

	private static IChangeDetail mergeModifyChangeDetails(AbstractModifyChangeDetail source, AbstractModifyChangeDetail target)
	{
    String currentLanguage = CAFContext.current.getLanguage();
		for (Map.Entry<String,Object> item : source.getPropertyChanges().entrySet())
		{
			if(item.getValue() instanceof  AbstractModifyChangeDetail) {
					Object targetPropertyChange = target.getPropertyChanges().get(item.getKey());
					if (targetPropertyChange != null && targetPropertyChange instanceof AbstractModifyChangeDetail) {
						mergeModifyChangeDetails((AbstractModifyChangeDetail) item.getValue(), (AbstractModifyChangeDetail) targetPropertyChange);
					} else {
						target.setItem(item.getKey(), ((AbstractModifyChangeDetail) item.getValue()).clone());
					}
			}
			else {
				target.setItem(item.getKey(), item.getValue());
        // 通过普通变更，更新多语属性中该值
        String multiPropName = item.getKey().concat(MultiLanguageInfo.MULTILANGUAGETOKEN);
        if (target.getMultiLanguageInfos().containsKey(multiPropName)) {
          target.getMultiLanguageInfos().get(multiPropName).getPropValueMap()
              .put(currentLanguage, item.getValue());
        }
			}
		}

		// 通过多语属性变更，更新普通变更
    InnerUtil.mergeMultiLanguageInfoToChanges(source.getMultiLanguageInfos(),
        target.getMultiLanguageInfos(), target.getPropertyChanges());

    if (source instanceof ValueObjModifyChangeDetail && target instanceof ValueObjModifyChangeDetail) {
      ((ValueObjModifyChangeDetail)target).setData(((ValueObjModifyChangeDetail)source).getData());
    }

		ModifyChangeDetail modifyChange = (ModifyChangeDetail)((source instanceof ModifyChangeDetail) ? source : null);
		if(modifyChange==null||modifyChange.getChildChanges()==null)
			return target;
		for (Map.Entry<String, java.util.Map<String, IChangeDetail>> childChange : modifyChange.getChildChanges().entrySet())
		{
			if (((ModifyChangeDetail)target).getChildChanges().containsKey(childChange.getKey()) == false)
			{
				((ModifyChangeDetail)target).getChildChanges().put(childChange.getKey(), cloneChildChange(childChange.getValue()));
			}
			else
			{
				mergeChildChanges(childChange.getValue(), ((ModifyChangeDetail)target).getChildChanges().get(childChange.getKey()));
			}
		}
		return target;
	}

	private static Map<String,IChangeDetail> cloneChildChange(Map<String,IChangeDetail> source)
	{
		if(source==null)
			return null;
		Map<String,IChangeDetail> target = new LinkedHashMap<>((int)(source.size()/0.75F+1F)) ;
		for (Map.Entry<String,IChangeDetail> entry:source.entrySet()) {
			target.put(entry.getKey(),entry.getValue().clone());
		}
		return  target;
	}

	private static void mergeChildChanges(java.util.Map<String, IChangeDetail> sourceDict, java.util.Map<String, IChangeDetail> targetDict)
	{
		for (Map.Entry<String, IChangeDetail> item : sourceDict.entrySet()) {
			IChangeDetail targetChange = targetDict.get(item.getKey());
			if (targetChange == null) {
				targetDict.put(item.getKey(), item.getValue().clone());
				continue;
			}
			IChangeDetail mergedChange = mergeChangeDetail(item.getValue(), targetChange);
			if (mergedChange == null) {
				targetDict.remove(item.getKey());
			} else {
				targetDict.put(item.getKey(), mergedChange);
			}
		}
	}

	private static IChangeDetail mergeChangeByDeletedSource(DeleteChangeDetail source, IChangeDetail target)
	{
		switch (target.getChangeType())
		{
			case Added:
				return null;
			case Modify:
			case Deleted:
				return source;
			default:
				throw new IllegalArgumentException();
		}
	}
}
